#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include<sys/wait.h>
//读取确切的字节数count的readn方法
ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count; //剩余的字节数
    size_t nread;         //已经读取的字节数

    char *bufp = (char *)buf;
    //因为不能够保证一次能够读到多少字节，所以循环读取，直到读完
    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR) //被信号中断，不认为出错
            {
                continue;
            }
            else
                return -1;
        }
        else if (nread == 0)
        {
            return count - nleft; //已经读取的字节数
        }

        bufp += nread;
        nleft -= nread;
    }
    return count;
}

//从sockfd的数据到buf中，并没有挪走sockfd数据
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
    while (1)
    {
        int ret = recv(sockfd, buf, len, MSG_PEEK);
        if (ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}
//读取一行数据，碰到\n就返回，maxline时一行中最多字符的个数
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
    int ret;
    int nread;
    int nleft = maxline;
    char *bufp = (char *)buf;

    while (1)
    {
        //查看sockfd的数据
        ret = recv_peek(sockfd, bufp, nleft);
        if (ret < 0)
            return ret;
        else if (ret == 0)
            return ret;
        //判断查看数据是否\n
        nread = ret;
        int i;
        for (i = 0; i < nread; i++)
        {
            if (bufp[i] == '\n') //有\n 就读走
            {
                ret = readn(sockfd, bufp, i + 1);
                if (ret != i + 1)
                {
                    perror("readn");
                    exit(1);
                }
                return ret;
            }
        }
        // recv_peek的内容，没有包含\n的处理方式
        if (nread > nleft)
        {
            exit(1);
        }
        nleft -= nread;

        ret = readn(sockfd, bufp, nread);
        if (ret != nread)
        {
            perror("readn");
            exit(1);
        }
        bufp += nread;
    }
    return -1;
}
int main()
{
    signal(SIGCHLD, SIG_IGN);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket:");
        exit(1);
    }

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(5188);
    //设置地址可以重复利用
    int on = 1;
    if ((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
    {
        perror("setsockopt ");
        exit(1);
    }
    int ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (ret < 0)
    {
        perror("bind");
        exit(1);
    }

    ret = listen(listenfd, SOMAXCONN);
    if (ret < 0)
    {
        perror("listen");
        exit(1);
    }
    struct sockaddr_in peeraddr;
    socklen_t peeraddrlen = sizeof(peeraddr);
    while (1)
    {
        int connfd = accept(listenfd, (struct sockaddr *)&peeraddr, &peeraddrlen);
        if (connfd < 0)
        {
            perror("accept");
            exit(1);
        }
        printf("ip:%s port:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

        pid_t pid = fork(); //创建子进程
        if (pid < 0)
        {
            perror("fork");
            exit(1);
        }
        else if (pid == 0) //子进程处理通信过程
        {
            close(listenfd); //子进程会复制父进程的listened，子进程不需要使用，关闭它即可
            char recvbuf[1024];
            int n;
            while (1)
            {
                memset(recvbuf, 0, sizeof(recvbuf));
                int sz = readline(connfd, recvbuf, sizeof(recvbuf)); //服务器接收客户端发来的数据,先接收包体的长度
                if (sz == -1)
                {
                    perror("readline");
                    exit(1);
                }
                if (sz == 0)
                {
                    printf("peer colse \n");
                    break;
                }
                printf("service accept client: ");
                fputs(recvbuf, stdout); //服务器打印客户端发来的数据

                write(connfd, recvbuf, strlen(recvbuf)); //服务端回射给客户端
            }

            exit(1); //客户端关闭，就会跳出到这行,该为客户端服务的子进程就没有存在的价值了


        }
        close(connfd);
    }

    return 0;
}